/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.awt;

import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import sun.awt.SunHints;
import java.lang.ref.WeakReference;

/**
 * The {@code RenderingHints} class defines and manages collections of
 * keys and associated values which allow an application to provide input
 * into the choice of algorithms used by other classes which perform
 * rendering and image manipulation services.
 * The {@link java.awt.Graphics2D} class, and classes that implement
 * {@link java.awt.image.BufferedImageOp} and
 * {@link java.awt.image.RasterOp} all provide methods to get and
 * possibly to set individual or groups of {@code RenderingHints}
 * keys and their associated values.
 * When those implementations perform any rendering or image manipulation
 * operations they should examine the values of any {@code RenderingHints}
 * that were requested by the caller and tailor the algorithms used
 * accordingly and to the best of their ability.
 * <p>
 * Note that since these keys and values are <i>hints</i>, there is
 * no requirement that a given implementation supports all possible
 * choices indicated below or that it can respond to requests to
 * modify its choice of algorithm.
 * The values of the various hint keys may also interact such that
 * while all variants of a given key are supported in one situation,
 * the implementation may be more restricted when the values associated
 * with other keys are modified.
 * For example, some implementations may be able to provide several
 * types of dithering when the antialiasing hint is turned off, but
 * have little control over dithering when antialiasing is on.
 * The full set of supported keys and hints may also vary by destination
 * since runtimes may use different underlying modules to render to
 * the screen, or to {@link java.awt.image.BufferedImage} objects,
 * or while printing.
 * <p>
 * Implementations are free to ignore the hints completely, but should
 * try to use an implementation algorithm that is as close as possible
 * to the request.
 * If an implementation supports a given algorithm when any value is used
 * for an associated hint key, then minimally it must do so when the
 * value for that key is the exact value that specifies the algorithm.
 * <p>
 * The keys used to control the hints are all special values that
 * subclass the associated {@link RenderingHints.Key} class.
 * Many common hints are expressed below as static constants in this
 * class, but the list is not meant to be exhaustive.
 * Other hints may be created by other packages by defining new objects
 * which subclass the {@code Key} class and defining the associated values.
 */
public class RenderingHints
    implements Map<Object, Object>, Cloneable {

  /**
   * Defines the base type of all keys used along with the
   * {@link RenderingHints} class to control various
   * algorithm choices in the rendering and imaging pipelines.
   * Instances of this class are immutable and unique which
   * means that tests for matches can be made using the
   * {@code ==} operator instead of the more expensive
   * {@code equals()} method.
   */
  public abstract static class Key {

    private static HashMap<Object, Object> identitymap = new HashMap<>(17);

    private String getIdentity() {
      // Note that the identity string is dependent on 3 variables:
      //     - the name of the subclass of Key
      //     - the identityHashCode of the subclass of Key
      //     - the integer key of the Key
      // It is theoretically possible for 2 distinct keys to collide
      // along all 3 of those attributes in the context of multiple
      // class loaders, but that occurrence will be extremely rare and
      // we account for that possibility below in the recordIdentity
      // method by slightly relaxing our uniqueness guarantees if we
      // end up in that situation.
      return getClass().getName() + "@" +
          Integer.toHexString(System.identityHashCode(getClass())) + ":" +
          Integer.toHexString(privatekey);
    }

    private synchronized static void recordIdentity(Key k) {
      Object identity = k.getIdentity();
      Object otherref = identitymap.get(identity);
      if (otherref != null) {
        Key otherkey = (Key) ((WeakReference) otherref).get();
        if (otherkey != null && otherkey.getClass() == k.getClass()) {
          throw new IllegalArgumentException(identity +
              " already registered");
        }
        // Note that this system can fail in a mostly harmless
        // way.  If we end up generating the same identity
        // String for 2 different classes (a very rare case)
        // then we correctly avoid throwing the exception above,
        // but we are about to drop through to a statement that
        // will replace the entry for the old Key subclass with
        // an entry for the new Key subclass.  At that time the
        // old subclass will be vulnerable to someone generating
        // a duplicate Key instance for it.  We could bail out
        // of the method here and let the old identity keep its
        // record in the map, but we are more likely to see a
        // duplicate key go by for the new class than the old
        // one since the new one is probably still in the
        // initialization stage.  In either case, the probability
        // of loading 2 classes in the same VM with the same name
        // and identityHashCode should be nearly impossible.
      }
      // Note: Use a weak reference to avoid holding on to extra
      // objects and classes after they should be unloaded.
      identitymap.put(identity, new WeakReference<Key>(k));
    }

    private int privatekey;

    /**
     * Construct a key using the indicated private key.  Each
     * subclass of Key maintains its own unique domain of integer
     * keys.  No two objects with the same integer key and of the
     * same specific subclass can be constructed.  An exception
     * will be thrown if an attempt is made to construct another
     * object of a given class with the same integer key as a
     * pre-existing instance of that subclass of Key.
     *
     * @param privatekey the specified key
     */
    protected Key(int privatekey) {
      this.privatekey = privatekey;
      recordIdentity(this);
    }

    /**
     * Returns true if the specified object is a valid value
     * for this Key.
     *
     * @param val the <code>Object</code> to test for validity
     * @return <code>true</code> if <code>val</code> is valid; <code>false</code> otherwise.
     */
    public abstract boolean isCompatibleValue(Object val);

    /**
     * Returns the private integer key that the subclass
     * instantiated this Key with.
     *
     * @return the private integer key that the subclass instantiated this Key with.
     */
    protected final int intKey() {
      return privatekey;
    }

    /**
     * The hash code for all Key objects will be the same as the
     * system identity code of the object as defined by the
     * System.identityHashCode() method.
     */
    public final int hashCode() {
      return super.hashCode();
    }

    /**
     * The equals method for all Key objects will return the same
     * result as the equality operator '=='.
     */
    public final boolean equals(Object o) {
      return this == o;
    }
  }

  HashMap<Object, Object> hintmap = new HashMap<>(7);

  /**
   * Antialiasing hint key.
   * The {@code ANTIALIASING} hint controls whether or not the
   * geometry rendering methods of a {@link Graphics2D} object
   * will attempt to reduce aliasing artifacts along the edges
   * of shapes.
   * <p>
   * A typical antialiasing algorithm works by blending the existing
   * colors of the pixels along the boundary of a shape with the
   * requested fill paint according to the estimated partial pixel
   * coverage of the shape.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_ANTIALIAS_ON}
   * <li>{@link #VALUE_ANTIALIAS_OFF}
   * <li>{@link #VALUE_ANTIALIAS_DEFAULT}
   * </ul>
   */
  public static final Key KEY_ANTIALIASING =
      SunHints.KEY_ANTIALIASING;

  /**
   * Antialiasing hint value -- rendering is done with antialiasing.
   *
   * @see #KEY_ANTIALIASING
   */
  public static final Object VALUE_ANTIALIAS_ON =
      SunHints.VALUE_ANTIALIAS_ON;

  /**
   * Antialiasing hint value -- rendering is done without antialiasing.
   *
   * @see #KEY_ANTIALIASING
   */
  public static final Object VALUE_ANTIALIAS_OFF =
      SunHints.VALUE_ANTIALIAS_OFF;

  /**
   * Antialiasing hint value -- rendering is done with a default
   * antialiasing mode chosen by the implementation.
   *
   * @see #KEY_ANTIALIASING
   */
  public static final Object VALUE_ANTIALIAS_DEFAULT =
      SunHints.VALUE_ANTIALIAS_DEFAULT;

  /**
   * Rendering hint key.
   * The {@code RENDERING} hint is a general hint that provides
   * a high level recommendation as to whether to bias algorithm
   * choices more for speed or quality when evaluating tradeoffs.
   * This hint could be consulted for any rendering or image
   * manipulation operation, but decisions will usually honor
   * other, more specific hints in preference to this hint.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_RENDER_SPEED}
   * <li>{@link #VALUE_RENDER_QUALITY}
   * <li>{@link #VALUE_RENDER_DEFAULT}
   * </ul>
   */
  public static final Key KEY_RENDERING =
      SunHints.KEY_RENDERING;

  /**
   * Rendering hint value -- rendering algorithms are chosen
   * with a preference for output speed.
   *
   * @see #KEY_RENDERING
   */
  public static final Object VALUE_RENDER_SPEED =
      SunHints.VALUE_RENDER_SPEED;

  /**
   * Rendering hint value -- rendering algorithms are chosen
   * with a preference for output quality.
   *
   * @see #KEY_RENDERING
   */
  public static final Object VALUE_RENDER_QUALITY =
      SunHints.VALUE_RENDER_QUALITY;

  /**
   * Rendering hint value -- rendering algorithms are chosen
   * by the implementation for a good tradeoff of performance
   * vs. quality.
   *
   * @see #KEY_RENDERING
   */
  public static final Object VALUE_RENDER_DEFAULT =
      SunHints.VALUE_RENDER_DEFAULT;

  /**
   * Dithering hint key.
   * The {@code DITHERING} hint controls how closely to approximate
   * a color when storing into a destination with limited color
   * resolution.
   * <p>
   * Some rendering destinations may support a limited number of
   * color choices which may not be able to accurately represent
   * the full spectrum of colors that can result during rendering
   * operations.
   * For such a destination the {@code DITHERING} hint controls
   * whether rendering is done with a flat solid fill of a single
   * pixel value which is the closest supported color to what was
   * requested, or whether shapes will be filled with a pattern of
   * colors which combine to better approximate that color.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_DITHER_DISABLE}
   * <li>{@link #VALUE_DITHER_ENABLE}
   * <li>{@link #VALUE_DITHER_DEFAULT}
   * </ul>
   */
  public static final Key KEY_DITHERING =
      SunHints.KEY_DITHERING;

  /**
   * Dithering hint value -- do not dither when rendering geometry.
   *
   * @see #KEY_DITHERING
   */
  public static final Object VALUE_DITHER_DISABLE =
      SunHints.VALUE_DITHER_DISABLE;

  /**
   * Dithering hint value -- dither when rendering geometry, if needed.
   *
   * @see #KEY_DITHERING
   */
  public static final Object VALUE_DITHER_ENABLE =
      SunHints.VALUE_DITHER_ENABLE;

  /**
   * Dithering hint value -- use a default for dithering chosen by
   * the implementation.
   *
   * @see #KEY_DITHERING
   */
  public static final Object VALUE_DITHER_DEFAULT =
      SunHints.VALUE_DITHER_DEFAULT;

  /**
   * Text antialiasing hint key.
   * The {@code TEXT_ANTIALIASING} hint can control the use of
   * antialiasing algorithms for text independently of the
   * choice used for shape rendering.
   * Often an application may want to use antialiasing for text
   * only and not for other shapes.
   * Additionally, the algorithms for reducing the aliasing
   * artifacts for text are often more sophisticated than those
   * that have been developed for general rendering so this
   * hint key provides additional values which can control
   * the choices of some of those text-specific algorithms.
   * If left in the {@code DEFAULT} state, this hint will
   * generally defer to the value of the regular
   * {@link #KEY_ANTIALIASING} hint key.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_TEXT_ANTIALIAS_ON}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_OFF}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_DEFAULT}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_GASP}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_HBGR}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_VRGB}
   * <li>{@link #VALUE_TEXT_ANTIALIAS_LCD_VBGR}
   * </ul>
   */
  public static final Key KEY_TEXT_ANTIALIASING =
      SunHints.KEY_TEXT_ANTIALIASING;

  /**
   * Text antialiasing hint value -- text rendering is done with
   * some form of antialiasing.
   *
   * @see #KEY_TEXT_ANTIALIASING
   */
  public static final Object VALUE_TEXT_ANTIALIAS_ON =
      SunHints.VALUE_TEXT_ANTIALIAS_ON;

  /**
   * Text antialiasing hint value -- text rendering is done without
   * any form of antialiasing.
   *
   * @see #KEY_TEXT_ANTIALIASING
   */
  public static final Object VALUE_TEXT_ANTIALIAS_OFF =
      SunHints.VALUE_TEXT_ANTIALIAS_OFF;

  /**
   * Text antialiasing hint value -- text rendering is done according
   * to the {@link #KEY_ANTIALIASING} hint or a default chosen by the
   * implementation.
   *
   * @see #KEY_TEXT_ANTIALIASING
   */
  public static final Object VALUE_TEXT_ANTIALIAS_DEFAULT =
      SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT;

  /**
   * Text antialiasing hint value -- text rendering is requested to
   * use information in the font resource which specifies for each point
   * size whether to apply {@link #VALUE_TEXT_ANTIALIAS_ON} or
   * {@link #VALUE_TEXT_ANTIALIAS_OFF}.
   * <p>
   * TrueType fonts typically provide this information in the 'gasp' table.
   * In the absence of this information, the behaviour for a particular
   * font and size is determined by implementation defaults.
   * <p>
   * <i>Note:</i>A font designer will typically carefully hint a font for
   * the most common user interface point sizes. Consequently the 'gasp'
   * table will likely specify to use only hinting at those sizes and not
   * "smoothing". So in many cases the resulting text display is
   * equivalent to {@code VALUE_TEXT_ANTIALIAS_OFF}.
   * This may be unexpected but is correct.
   * <p>
   * Logical fonts which are composed of multiple physical fonts will for
   * consistency will use the setting most appropriate for the overall
   * composite font.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Object VALUE_TEXT_ANTIALIAS_GASP =
      SunHints.VALUE_TEXT_ANTIALIAS_GASP;

  /**
   * Text antialiasing hint value -- request that text be displayed
   * optimised for an LCD display with subpixels in order from display
   * left to right of R,G,B such that the horizontal subpixel resolution
   * is three times that of the full pixel horizontal resolution (HRGB).
   * This is the most common configuration.
   * Selecting this hint for displays with one of the other LCD subpixel
   * configurations will likely result in unfocused text.
   * <p>
   * <i>Notes:</i><br>
   * An implementation when choosing whether to apply any of the
   * LCD text hint values may take into account factors including requiring
   * color depth of the destination to be at least 15 bits per pixel
   * (ie 5 bits per color component),
   * characteristics of a font such as whether embedded bitmaps may
   * produce better results, or when displaying to a non-local networked
   * display device enabling it only if suitable protocols are available,
   * or ignoring the hint if performing very high resolution rendering
   * or the target device is not appropriate: eg when printing.
   * <p>
   * These hints can equally be applied when rendering to software images,
   * but these images may not then be suitable for general export, as the
   * text will have been rendered appropriately for a specific subpixel
   * organisation. Also lossy images are not a good choice, nor image
   * formats such as GIF which have limited colors.
   * So unless the image is destined solely for rendering on a
   * display device with the same configuration, some other text
   * anti-aliasing hint such as
   * {@link #VALUE_TEXT_ANTIALIAS_ON}
   * may be a better choice.
   * <p>Selecting a value which does not match the LCD display in use
   * will likely lead to a degradation in text quality.
   * On display devices (ie CRTs) which do not have the same characteristics
   * as LCD displays, the overall effect may appear similar to standard text
   * anti-aliasing, but the quality may be degraded by color distortion.
   * Analog connected LCD displays may also show little advantage over
   * standard text-antialiasing and be similar to CRTs.
   * <p>
   * In other words for the best results use an LCD display with a digital
   * display connector and specify the appropriate sub-pixel configuration.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Object VALUE_TEXT_ANTIALIAS_LCD_HRGB =
      SunHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;

  /**
   * Text antialiasing hint value -- request that text be displayed
   * optimised for an LCD display with subpixels in order from display
   * left to right of B,G,R such that the horizontal subpixel resolution
   * is three times that of the full pixel horizontal resolution (HBGR).
   * This is a much less common configuration than HRGB.
   * Selecting this hint for displays with one of the other LCD subpixel
   * configurations will likely result in unfocused text.
   * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
   * for more information on when this hint is applied.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Object VALUE_TEXT_ANTIALIAS_LCD_HBGR =
      SunHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;

  /**
   * Text antialiasing hint value -- request that text be displayed
   * optimised for an LCD display with subpixel organisation from display
   * top to bottom of R,G,B such that the vertical subpixel resolution is
   * three times that of the full pixel vertical resolution (VRGB).
   * Vertical orientation is very uncommon and probably mainly useful
   * for a physically rotated display.
   * Selecting this hint for displays with one of the other LCD subpixel
   * configurations will likely result in unfocused text.
   * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
   * for more information on when this hint is applied.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Object VALUE_TEXT_ANTIALIAS_LCD_VRGB =
      SunHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB;

  /**
   * Text antialiasing hint value -- request that text be displayed
   * optimised for an LCD display with subpixel organisation from display
   * top to bottom of B,G,R such that the vertical subpixel resolution is
   * three times that of the full pixel vertical resolution (VBGR).
   * Vertical orientation is very uncommon and probably mainly useful
   * for a physically rotated display.
   * Selecting this hint for displays with one of the other LCD subpixel
   * configurations will likely result in unfocused text.
   * See {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB},
   * for more information on when this hint is applied.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Object VALUE_TEXT_ANTIALIAS_LCD_VBGR =
      SunHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR;


  /**
   * LCD text contrast rendering hint key.
   * The value is an <code>Integer</code> object which is used as a text
   * contrast adjustment when used in conjunction with an LCD text
   * anti-aliasing hint such as
   * {@link #VALUE_TEXT_ANTIALIAS_LCD_HRGB}.
   * <ul>
   * <li>Values should be a positive integer in the range 100 to 250.
   * <li>A lower value (eg 100) corresponds to higher contrast text when
   * displaying dark text on a light background.
   * <li>A higher value (eg 200) corresponds to lower contrast text when
   * displaying dark text on a light background.
   * <li>A typical useful value is in the narrow range 140-180.
   * <li>If no value is specified, a system or implementation default value
   * will be applied.
   * </ul>
   * The default value can be expected to be adequate for most purposes,
   * so clients should rarely need to specify a value for this hint unless
   * they have concrete information as to an appropriate value.
   * A higher value does not mean a higher contrast, in fact the opposite
   * is true.
   * The correction is applied in a similar manner to a gamma adjustment
   * for non-linear perceptual luminance response of display systems, but
   * does not indicate a full correction for this.
   *
   * @see #KEY_TEXT_ANTIALIASING
   * @since 1.6
   */
  public static final Key KEY_TEXT_LCD_CONTRAST =
      SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST;

  /**
   * Font fractional metrics hint key.
   * The {@code FRACTIONALMETRICS} hint controls whether the positioning
   * of individual character glyphs takes into account the sub-pixel
   * accuracy of the scaled character advances of the font or whether
   * such advance vectors are rounded to an integer number of whole
   * device pixels.
   * This hint only recommends how much accuracy should be used to
   * position the glyphs and does not specify or recommend whether or
   * not the actual rasterization or pixel bounds of the glyph should
   * be modified to match.
   * <p>
   * Rendering text to a low resolution device like a screen will
   * necessarily involve a number of rounding operations as the
   * high quality and very precise definition of the shape and
   * metrics of the character glyphs must be matched to discrete
   * device pixels.
   * Ideally the positioning of glyphs during text layout would be
   * calculated by scaling the design metrics in the font according
   * to the point size, but then the scaled advance width will not
   * necessarily be an integer number of pixels.
   * If the glyphs are positioned with sub-pixel accuracy according
   * to these scaled design metrics then the rasterization would
   * ideally need to be adjusted for each possible sub-pixel origin.
   * <p>
   * Unfortunately, scaling each glyph customized to its exact
   * subpixel origin during text layout would be prohibitively
   * expensive so a simplified system based on integer device
   * positions is typically used to lay out the text.
   * The rasterization of the glyph and the scaled advance width
   * are both adjusted together to yield text that looks good at
   * device resolution and has consistent integer pixel distances
   * between glyphs that help the glyphs look uniformly and
   * consistently spaced and readable.
   * <p>
   * This process of rounding advance widths for rasterized glyphs
   * to integer distances means that the character density and the
   * overall length of a string of text will be different from the
   * theoretical design measurements due to the accumulation of
   * a series of small differences in the adjusted widths of
   * each glyph.
   * The specific differences will be different for each glyph,
   * some being wider and some being narrower than their theoretical
   * design measurements.
   * Thus the overall difference in character density and length
   * will vary by a number of factors including the font, the
   * specific device resolution being targeted, and the glyphs
   * chosen to represent the string being rendered.
   * As a result, rendering the same string at multiple device
   * resolutions can yield widely varying metrics for whole strings.
   * <p>
   * When {@code FRACTIONALMETRICS} are enabled, the true font design
   * metrics are scaled by the point size and used for layout with
   * sub-pixel accuracy.
   * The average density of glyphs and total length of a long
   * string of characters will therefore more closely match the
   * theoretical design of the font, but readability may be affected
   * since individual pairs of characters may not always appear to
   * be consistent distances apart depending on how the sub-pixel
   * accumulation of the glyph origins meshes with the device pixel
   * grid.
   * Enabling this hint may be desirable when text layout is being
   * performed that must be consistent across a wide variety of
   * output resolutions.
   * Specifically, this hint may be desirable in situations where
   * the layout of text is being previewed on a low resolution
   * device like a screen for output that will eventually be
   * rendered on a high resolution printer or typesetting device.
   * <p>
   * When disabled, the scaled design metrics are rounded or adjusted
   * to integer distances for layout.
   * The distances between any specific pair of glyphs will be more
   * uniform on the device, but the density and total length of long
   * strings may no longer match the theoretical intentions of the
   * font designer.
   * Disabling this hint will typically produce more readable results
   * on low resolution devices like computer monitors.
   * <p>
   * The allowable values for this key are
   * <ul>
   * <li>{@link #VALUE_FRACTIONALMETRICS_OFF}
   * <li>{@link #VALUE_FRACTIONALMETRICS_ON}
   * <li>{@link #VALUE_FRACTIONALMETRICS_DEFAULT}
   * </ul>
   */
  public static final Key KEY_FRACTIONALMETRICS =
      SunHints.KEY_FRACTIONALMETRICS;

  /**
   * Font fractional metrics hint value -- character glyphs are
   * positioned with advance widths rounded to pixel boundaries.
   *
   * @see #KEY_FRACTIONALMETRICS
   */
  public static final Object VALUE_FRACTIONALMETRICS_OFF =
      SunHints.VALUE_FRACTIONALMETRICS_OFF;

  /**
   * Font fractional metrics hint value -- character glyphs are
   * positioned with sub-pixel accuracy.
   *
   * @see #KEY_FRACTIONALMETRICS
   */
  public static final Object VALUE_FRACTIONALMETRICS_ON =
      SunHints.VALUE_FRACTIONALMETRICS_ON;

  /**
   * Font fractional metrics hint value -- character glyphs are
   * positioned with accuracy chosen by the implementation.
   *
   * @see #KEY_FRACTIONALMETRICS
   */
  public static final Object VALUE_FRACTIONALMETRICS_DEFAULT =
      SunHints.VALUE_FRACTIONALMETRICS_DEFAULT;

  /**
   * Interpolation hint key.
   * The {@code INTERPOLATION} hint controls how image pixels are
   * filtered or resampled during an image rendering operation.
   * <p>
   * Implicitly images are defined to provide color samples at
   * integer coordinate locations.
   * When images are rendered upright with no scaling onto a
   * destination, the choice of which image pixels map to which
   * device pixels is obvious and the samples at the integer
   * coordinate locations in the image are transfered to the
   * pixels at the corresponding integer locations on the device
   * pixel grid one for one.
   * When images are rendered in a scaled, rotated, or otherwise
   * transformed coordinate system, then the mapping of device
   * pixel coordinates back to the image can raise the question
   * of what color sample to use for the continuous coordinates
   * that lie between the integer locations of the provided image
   * samples.
   * Interpolation algorithms define functions which provide a
   * color sample for any continuous coordinate in an image based
   * on the color samples at the surrounding integer coordinates.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR}
   * <li>{@link #VALUE_INTERPOLATION_BILINEAR}
   * <li>{@link #VALUE_INTERPOLATION_BICUBIC}
   * </ul>
   */
  public static final Key KEY_INTERPOLATION =
      SunHints.KEY_INTERPOLATION;

  /**
   * Interpolation hint value -- the color sample of the nearest
   * neighboring integer coordinate sample in the image is used.
   * Conceptually the image is viewed as a grid of unit-sized
   * square regions of color centered around the center of each
   * image pixel.
   * <p>
   * As the image is scaled up, it will look correspondingly blocky.
   * As the image is scaled down, the colors for source pixels will
   * be either used unmodified, or skipped entirely in the output
   * representation.
   *
   * @see #KEY_INTERPOLATION
   */
  public static final Object VALUE_INTERPOLATION_NEAREST_NEIGHBOR =
      SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;

  /**
   * Interpolation hint value -- the color samples of the 4 nearest
   * neighboring integer coordinate samples in the image are
   * interpolated linearly to produce a color sample.
   * Conceptually the image is viewed as a set of infinitely small
   * point color samples which have value only at the centers of
   * integer coordinate pixels and the space between those pixel
   * centers is filled with linear ramps of colors that connect
   * adjacent discrete samples in a straight line.
   * <p>
   * As the image is scaled up, there are no blocky edges between
   * the colors in the image as there are with
   * {@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR NEAREST_NEIGHBOR},
   * but the blending may show some subtle discontinuities along the
   * horizontal and vertical edges that line up with the samples
   * caused by a sudden change in the slope of the interpolation
   * from one side of a sample to the other.
   * As the image is scaled down, more image pixels have their
   * color samples represented in the resulting output since each
   * output pixel receives color information from up to 4 image
   * pixels.
   *
   * @see #KEY_INTERPOLATION
   */
  public static final Object VALUE_INTERPOLATION_BILINEAR =
      SunHints.VALUE_INTERPOLATION_BILINEAR;

  /**
   * Interpolation hint value -- the color samples of 9 nearby
   * integer coordinate samples in the image are interpolated using
   * a cubic function in both {@code X} and {@code Y} to produce
   * a color sample.
   * Conceptually the view of the image is very similar to the view
   * used in the {@link #VALUE_INTERPOLATION_BILINEAR BILINEAR}
   * algorithm except that the ramps of colors that connect between
   * the samples are curved and have better continuity of slope
   * as they cross over between sample boundaries.
   * <p>
   * As the image is scaled up, there are no blocky edges and the
   * interpolation should appear smoother and with better depictions
   * of any edges in the original image than with {@code BILINEAR}.
   * As the image is scaled down, even more of the original color
   * samples from the original image will have their color information
   * carried through and represented.
   *
   * @see #KEY_INTERPOLATION
   */
  public static final Object VALUE_INTERPOLATION_BICUBIC =
      SunHints.VALUE_INTERPOLATION_BICUBIC;

  /**
   * Alpha interpolation hint key.
   * The {@code ALPHA_INTERPOLATION} hint is a general hint that
   * provides a high level recommendation as to whether to bias
   * alpha blending algorithm choices more for speed or quality
   * when evaluating tradeoffs.
   * <p>
   * This hint could control the choice of alpha blending
   * calculations that sacrifice some precision to use fast
   * lookup tables or lower precision SIMD instructions.
   * This hint could also control whether or not the color
   * and alpha values are converted into a linear color space
   * during the calculations for a more linear visual effect
   * at the expense of additional per-pixel calculations.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_ALPHA_INTERPOLATION_SPEED}
   * <li>{@link #VALUE_ALPHA_INTERPOLATION_QUALITY}
   * <li>{@link #VALUE_ALPHA_INTERPOLATION_DEFAULT}
   * </ul>
   */
  public static final Key KEY_ALPHA_INTERPOLATION =
      SunHints.KEY_ALPHA_INTERPOLATION;

  /**
   * Alpha interpolation hint value -- alpha blending algorithms
   * are chosen with a preference for calculation speed.
   *
   * @see #KEY_ALPHA_INTERPOLATION
   */
  public static final Object VALUE_ALPHA_INTERPOLATION_SPEED =
      SunHints.VALUE_ALPHA_INTERPOLATION_SPEED;

  /**
   * Alpha interpolation hint value -- alpha blending algorithms
   * are chosen with a preference for precision and visual quality.
   *
   * @see #KEY_ALPHA_INTERPOLATION
   */
  public static final Object VALUE_ALPHA_INTERPOLATION_QUALITY =
      SunHints.VALUE_ALPHA_INTERPOLATION_QUALITY;

  /**
   * Alpha interpolation hint value -- alpha blending algorithms
   * are chosen by the implementation for a good tradeoff of
   * performance vs. quality.
   *
   * @see #KEY_ALPHA_INTERPOLATION
   */
  public static final Object VALUE_ALPHA_INTERPOLATION_DEFAULT =
      SunHints.VALUE_ALPHA_INTERPOLATION_DEFAULT;

  /**
   * Color rendering hint key.
   * The {@code COLOR_RENDERING} hint controls the accuracy of
   * approximation and conversion when storing colors into a
   * destination image or surface.
   * <p>
   * When a rendering or image manipulation operation produces
   * a color value that must be stored into a destination, it
   * must first convert that color into a form suitable for
   * storing into the destination image or surface.
   * Minimally, the color components must be converted to bit
   * representations and ordered in the correct order or an
   * index into a color lookup table must be chosen before
   * the data can be stored into the destination memory.
   * Without this minimal conversion, the data in the destination
   * would likely represent random, incorrect or possibly even
   * unsupported values.
   * Algorithms to quickly convert the results of rendering
   * operations into the color format of most common destinations
   * are well known and fairly optimal to execute.
   * <p>
   * Simply performing the most basic color format conversion to
   * store colors into a destination can potentially ignore a
   * difference in the calibration of the
   * {@link java.awt.color.ColorSpace}
   * of the source and destination or other factors such as the
   * linearity of the gamma correction.
   * Unless the source and destination {@code ColorSpace} are
   * identical, to correctly perform a rendering operation with
   * the most care taken for the accuracy of the colors being
   * represented, the source colors should be converted to a
   * device independent {@code ColorSpace} and the results then
   * converted back to the destination {@code ColorSpace}.
   * Furthermore, if calculations such as the blending of multiple
   * source colors are to be performed during the rendering
   * operation, greater visual clarity can be achieved if the
   * intermediate device independent {@code ColorSpace} is
   * chosen to have a linear relationship between the values
   * being calculated and the perception of the human eye to
   * the response curves of the output device.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_COLOR_RENDER_SPEED}
   * <li>{@link #VALUE_COLOR_RENDER_QUALITY}
   * <li>{@link #VALUE_COLOR_RENDER_DEFAULT}
   * </ul>
   */
  public static final Key KEY_COLOR_RENDERING =
      SunHints.KEY_COLOR_RENDERING;

  /**
   * Color rendering hint value -- perform the fastest color
   * conversion to the format of the output device.
   *
   * @see #KEY_COLOR_RENDERING
   */
  public static final Object VALUE_COLOR_RENDER_SPEED =
      SunHints.VALUE_COLOR_RENDER_SPEED;

  /**
   * Color rendering hint value -- perform the color conversion
   * calculations with the highest accuracy and visual quality.
   *
   * @see #KEY_COLOR_RENDERING
   */
  public static final Object VALUE_COLOR_RENDER_QUALITY =
      SunHints.VALUE_COLOR_RENDER_QUALITY;

  /**
   * Color rendering hint value -- perform color conversion
   * calculations as chosen by the implementation to represent
   * the best available tradeoff between performance and
   * accuracy.
   *
   * @see #KEY_COLOR_RENDERING
   */
  public static final Object VALUE_COLOR_RENDER_DEFAULT =
      SunHints.VALUE_COLOR_RENDER_DEFAULT;

  /**
   * Stroke normalization control hint key.
   * The {@code STROKE_CONTROL} hint controls whether a rendering
   * implementation should or is allowed to modify the geometry
   * of rendered shapes for various purposes.
   * <p>
   * Some implementations may be able to use an optimized platform
   * rendering library which may be faster than traditional software
   * rendering algorithms on a given platform, but which may also
   * not support floating point coordinates.
   * Some implementations may also have sophisticated algorithms
   * which perturb the coordinates of a path so that wide lines
   * appear more uniform in width and spacing.
   * <p>
   * If an implementation performs any type of modification or
   * "normalization" of a path, it should never move the coordinates
   * by more than half a pixel in any direction.
   * <p>
   * The allowable values for this hint are
   * <ul>
   * <li>{@link #VALUE_STROKE_NORMALIZE}
   * <li>{@link #VALUE_STROKE_PURE}
   * <li>{@link #VALUE_STROKE_DEFAULT}
   * </ul>
   *
   * @since 1.3
   */
  public static final Key KEY_STROKE_CONTROL =
      SunHints.KEY_STROKE_CONTROL;

  /**
   * Stroke normalization control hint value -- geometry may be
   * modified or left pure depending on the tradeoffs in a given
   * implementation.
   * Typically this setting allows an implementation to use a fast
   * integer coordinate based platform rendering library, but does
   * not specifically request normalization for uniformity or
   * aesthetics.
   *
   * @see #KEY_STROKE_CONTROL
   * @since 1.3
   */
  public static final Object VALUE_STROKE_DEFAULT =
      SunHints.VALUE_STROKE_DEFAULT;

  /**
   * Stroke normalization control hint value -- geometry should
   * be normalized to improve uniformity or spacing of lines and
   * overall aesthetics.
   * Note that different normalization algorithms may be more
   * successful than others for given input paths.
   *
   * @see #KEY_STROKE_CONTROL
   * @since 1.3
   */
  public static final Object VALUE_STROKE_NORMALIZE =
      SunHints.VALUE_STROKE_NORMALIZE;

  /**
   * Stroke normalization control hint value -- geometry should
   * be left unmodified and rendered with sub-pixel accuracy.
   *
   * @see #KEY_STROKE_CONTROL
   * @since 1.3
   */
  public static final Object VALUE_STROKE_PURE =
      SunHints.VALUE_STROKE_PURE;

  /**
   * Constructs a new object with keys and values initialized
   * from the specified Map object which may be null.
   *
   * @param init a map of key/value pairs to initialize the hints or null if the object should be
   * empty
   */
  public RenderingHints(Map<Key, ?> init) {
    if (init != null) {
      hintmap.putAll(init);
    }
  }

  /**
   * Constructs a new object with the specified key/value pair.
   *
   * @param key the key of the particular hint property
   * @param value the value of the hint property specified with <code>key</code>
   */
  public RenderingHints(Key key, Object value) {
    hintmap.put(key, value);
  }

  /**
   * Returns the number of key-value mappings in this
   * <code>RenderingHints</code>.
   *
   * @return the number of key-value mappings in this <code>RenderingHints</code>.
   */
  public int size() {
    return hintmap.size();
  }

  /**
   * Returns <code>true</code> if this
   * <code>RenderingHints</code> contains no key-value mappings.
   *
   * @return <code>true</code> if this <code>RenderingHints</code> contains no key-value mappings.
   */
  public boolean isEmpty() {
    return hintmap.isEmpty();
  }

  /**
   * Returns {@code true} if this {@code RenderingHints}
   * contains a mapping for the specified key.
   *
   * @param key key whose presence in this {@code RenderingHints} is to be tested.
   * @return {@code true} if this {@code RenderingHints} contains a mapping for the specified key.
   * @throws ClassCastException if the key can not be cast to {@code RenderingHints.Key}
   */
  public boolean containsKey(Object key) {
    return hintmap.containsKey((Key) key);
  }

  /**
   * Returns true if this RenderingHints maps one or more keys to the
   * specified value.
   * More formally, returns <code>true</code> if and only
   * if this <code>RenderingHints</code>
   * contains at least one mapping to a value <code>v</code> such that
   * <pre>
   * (value==null ? v==null : value.equals(v))
   * </pre>.
   * This operation will probably require time linear in the
   * <code>RenderingHints</code> size for most implementations
   * of <code>RenderingHints</code>.
   *
   * @param value value whose presence in this <code>RenderingHints</code> is to be tested.
   * @return <code>true</code> if this <code>RenderingHints</code> maps one or more keys to the
   * specified value.
   */
  public boolean containsValue(Object value) {
    return hintmap.containsValue(value);
  }

  /**
   * Returns the value to which the specified key is mapped.
   *
   * @param key a rendering hint key
   * @return the value to which the key is mapped in this object or {@code null} if the key is not
   * mapped to any value in this object.
   * @throws ClassCastException if the key can not be cast to {@code RenderingHints.Key}
   * @see #put(Object, Object)
   */
  public Object get(Object key) {
    return hintmap.get((Key) key);
  }

  /**
   * Maps the specified {@code key} to the specified
   * {@code value} in this {@code RenderingHints} object.
   * Neither the key nor the value can be {@code null}.
   * The value can be retrieved by calling the {@code get} method
   * with a key that is equal to the original key.
   *
   * @param key the rendering hint key.
   * @param value the rendering hint value.
   * @return the previous value of the specified key in this object or {@code null} if it did not
   * have one.
   * @throws NullPointerException if the key is {@code null}.
   * @throws ClassCastException if the key can not be cast to {@code RenderingHints.Key}
   * @throws IllegalArgumentException if the {@link Key#isCompatibleValue(java.lang.Object)
   * Key.isCompatibleValue()} method of the specified key returns false for the specified value
   * @see #get(Object)
   */
  public Object put(Object key, Object value) {
    if (!((Key) key).isCompatibleValue(value)) {
      throw new IllegalArgumentException(value +
          " incompatible with " +
          key);
    }
    return hintmap.put((Key) key, value);
  }

  /**
   * Adds all of the keys and corresponding values from the specified
   * <code>RenderingHints</code> object to this
   * <code>RenderingHints</code> object. Keys that are present in
   * this <code>RenderingHints</code> object, but not in the specified
   * <code>RenderingHints</code> object are not affected.
   *
   * @param hints the set of key/value pairs to be added to this <code>RenderingHints</code> object
   */
  public void add(RenderingHints hints) {
    hintmap.putAll(hints.hintmap);
  }

  /**
   * Clears this <code>RenderingHints</code> object of all key/value
   * pairs.
   */
  public void clear() {
    hintmap.clear();
  }

  /**
   * Removes the key and its corresponding value from this
   * {@code RenderingHints} object. This method does nothing if the
   * key is not in this {@code RenderingHints} object.
   *
   * @param key the rendering hints key that needs to be removed
   * @return the value to which the key had previously been mapped in this {@code RenderingHints}
   * object, or {@code null} if the key did not have a mapping.
   * @throws ClassCastException if the key can not be cast to {@code RenderingHints.Key}
   */
  public Object remove(Object key) {
    return hintmap.remove((Key) key);
  }

  /**
   * Copies all of the mappings from the specified {@code Map}
   * to this {@code RenderingHints}.  These mappings replace
   * any mappings that this {@code RenderingHints} had for any
   * of the keys currently in the specified {@code Map}.
   *
   * @param m the specified {@code Map}
   * @throws ClassCastException class of a key or value in the specified {@code Map} prevents it
   * from being stored in this {@code RenderingHints}.
   * @throws IllegalArgumentException some aspect of a key or value in the specified {@code Map}
   * prevents it from being stored in this {@code RenderingHints}.
   */
  public void putAll(Map<?, ?> m) {
    // ## javac bug?
    //if (m instanceof RenderingHints) {
    if (RenderingHints.class.isInstance(m)) {
      //hintmap.putAll(((RenderingHints) m).hintmap);
      for (Map.Entry<?, ?> entry : m.entrySet()) {
        hintmap.put(entry.getKey(), entry.getValue());
      }
    } else {
      // Funnel each key/value pair through our protected put method
      for (Map.Entry<?, ?> entry : m.entrySet()) {
        put(entry.getKey(), entry.getValue());
      }
    }
  }

  /**
   * Returns a <code>Set</code> view of the Keys contained in this
   * <code>RenderingHints</code>.  The Set is backed by the
   * <code>RenderingHints</code>, so changes to the
   * <code>RenderingHints</code> are reflected in the <code>Set</code>,
   * and vice-versa.  If the <code>RenderingHints</code> is modified
   * while an iteration over the <code>Set</code> is in progress,
   * the results of the iteration are undefined.  The <code>Set</code>
   * supports element removal, which removes the corresponding
   * mapping from the <code>RenderingHints</code>, via the
   * <code>Iterator.remove</code>, <code>Set.remove</code>,
   * <code>removeAll</code> <code>retainAll</code>, and
   * <code>clear</code> operations.  It does not support
   * the <code>add</code> or <code>addAll</code> operations.
   *
   * @return a <code>Set</code> view of the keys contained in this <code>RenderingHints</code>.
   */
  public Set<Object> keySet() {
    return hintmap.keySet();
  }

  /**
   * Returns a <code>Collection</code> view of the values
   * contained in this <code>RenderinHints</code>.
   * The <code>Collection</code> is backed by the
   * <code>RenderingHints</code>, so changes to
   * the <code>RenderingHints</code> are reflected in
   * the <code>Collection</code>, and vice-versa.
   * If the <code>RenderingHints</code> is modified while
   * an iteration over the <code>Collection</code> is
   * in progress, the results of the iteration are undefined.
   * The <code>Collection</code> supports element removal,
   * which removes the corresponding mapping from the
   * <code>RenderingHints</code>, via the
   * <code>Iterator.remove</code>,
   * <code>Collection.remove</code>, <code>removeAll</code>,
   * <code>retainAll</code> and <code>clear</code> operations.
   * It does not support the <code>add</code> or
   * <code>addAll</code> operations.
   *
   * @return a <code>Collection</code> view of the values contained in this
   * <code>RenderingHints</code>.
   */
  public Collection<Object> values() {
    return hintmap.values();
  }

  /**
   * Returns a <code>Set</code> view of the mappings contained
   * in this <code>RenderingHints</code>.  Each element in the
   * returned <code>Set</code> is a <code>Map.Entry</code>.
   * The <code>Set</code> is backed by the <code>RenderingHints</code>,
   * so changes to the <code>RenderingHints</code> are reflected
   * in the <code>Set</code>, and vice-versa.  If the
   * <code>RenderingHints</code> is modified while
   * while an iteration over the <code>Set</code> is in progress,
   * the results of the iteration are undefined.
   * <p>
   * The entrySet returned from a <code>RenderingHints</code> object
   * is not modifiable.
   *
   * @return a <code>Set</code> view of the mappings contained in this <code>RenderingHints</code>.
   */
  public Set<Map.Entry<Object, Object>> entrySet() {
    return Collections.unmodifiableMap(hintmap).entrySet();
  }

  /**
   * Compares the specified <code>Object</code> with this
   * <code>RenderingHints</code> for equality.
   * Returns <code>true</code> if the specified object is also a
   * <code>Map</code> and the two <code>Map</code> objects represent
   * the same mappings.  More formally, two <code>Map</code> objects
   * <code>t1</code> and <code>t2</code> represent the same mappings
   * if <code>t1.keySet().equals(t2.keySet())</code> and for every
   * key <code>k</code> in <code>t1.keySet()</code>,
   * <pre>
   * (t1.get(k)==null ? t2.get(k)==null : t1.get(k).equals(t2.get(k)))
   * </pre>.
   * This ensures that the <code>equals</code> method works properly across
   * different implementations of the <code>Map</code> interface.
   *
   * @param o <code>Object</code> to be compared for equality with this
   * <code>RenderingHints</code>.
   * @return <code>true</code> if the specified <code>Object</code> is equal to this
   * <code>RenderingHints</code>.
   */
  public boolean equals(Object o) {
    if (o instanceof RenderingHints) {
      return hintmap.equals(((RenderingHints) o).hintmap);
    } else if (o instanceof Map) {
      return hintmap.equals(o);
    }
    return false;
  }

  /**
   * Returns the hash code value for this <code>RenderingHints</code>.
   * The hash code of a <code>RenderingHints</code> is defined to be
   * the sum of the hashCodes of each <code>Entry</code> in the
   * <code>RenderingHints</code> object's entrySet view.  This ensures that
   * <code>t1.equals(t2)</code> implies that
   * <code>t1.hashCode()==t2.hashCode()</code> for any two <code>Map</code>
   * objects <code>t1</code> and <code>t2</code>, as required by the general
   * contract of <code>Object.hashCode</code>.
   *
   * @return the hash code value for this <code>RenderingHints</code>.
   * @see java.util.Map.Entry#hashCode()
   * @see Object#hashCode()
   * @see Object#equals(Object)
   * @see #equals(Object)
   */
  public int hashCode() {
    return hintmap.hashCode();
  }

  /**
   * Creates a clone of this <code>RenderingHints</code> object
   * that has the same contents as this <code>RenderingHints</code>
   * object.
   *
   * @return a clone of this instance.
   */
  @SuppressWarnings("unchecked")
  public Object clone() {
    RenderingHints rh;
    try {
      rh = (RenderingHints) super.clone();
      if (hintmap != null) {
        rh.hintmap = (HashMap<Object, Object>) hintmap.clone();
      }
    } catch (CloneNotSupportedException e) {
      // this shouldn't happen, since we are Cloneable
      throw new InternalError(e);
    }

    return rh;
  }

  /**
   * Returns a rather long string representation of the hashmap
   * which contains the mappings of keys to values for this
   * <code>RenderingHints</code> object.
   *
   * @return a string representation of this object.
   */
  public String toString() {
    if (hintmap == null) {
      return getClass().getName() + "@" +
          Integer.toHexString(hashCode()) +
          " (0 hints)";
    }

    return hintmap.toString();
  }
}
